package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"math"
	"runtime"
	"strings"
	"sync/atomic"
	"time"
)

/**
 *
 */
type GnssSVEpoch struct {
	Week uint32 // 如果为0需要使用引用时间
	TOW  uint32 // 周内毫秒

	IODS              byte
	ClkSteering       byte // 站钟调整策略标识 DF411 uint2 2   // Clock Steering NaviIndicator
	ExternalClk       byte // 外钟使用情况标识 DF412 uint2 2   // External Clock NaviIndicator
	SmoothingType     byte
	SmoothingInterval byte

	StaID uint16 // stationid

	obsMap [GNSS_SV_MAX_SYS - 1]*GnssSVObsList

	Tag interface{}
}

const (
	obsmap_len = GNSS_SV_MAX_SYS - 1
)

func gnsssvepoch_finalize(obj *GnssSVEpoch) {
	atomic.AddInt32(&gnss_sv_epoch_cnt, -1)
}

func NewGnssSVEpoch() *GnssSVEpoch {
	rval := &GnssSVEpoch{}
	runtime.SetFinalizer(rval, gnsssvepoch_finalize)
	atomic.AddInt32(&gnss_sv_epoch_cnt, 1)
	for i := U8_0; i < obsmap_len; i++ {
		rval.obsMap[i] = NewGnssSVObsList(i + 1)
	}
	return rval
}

func (this *GnssSVEpoch) ResetRoughR() {
	this.Range(func(navisys byte, yobs *GnssSVObs) bool {
		yobs.RoughR = 0
		return true
	})
}

func (this *GnssSVEpoch) Reset() {
	this.Week = 0
	this.TOW = 0
	for i := U8_0; i < obsmap_len; i++ {
		this.obsMap[i].Reset()
	}
}

func (this *GnssSVEpoch) DecodeTime(rt time.Time) (rval time.Time) {
	if rt.IsZero() {
		rval = TOW2Time(time.Now().UTC(), uint(this.TOW))
	} else {
		rval = TOW2Time(rt, uint(this.TOW))
	}
	this.Week, _ = DecodeGpsTime(rval)
	return
}

func (this *GnssSVEpoch) GetGpsTime(rt time.Time) time.Time {
	if this.Week > 0 {
		return TOW2TimeEx(uint(this.Week), uint(this.TOW))
	}
	if rt.IsZero() {
		return TOW2Time(time.Now().UTC(), uint(this.TOW))
	}

	return TOW2Time(rt, uint(this.TOW))
}

func (this *GnssSVEpoch) StatNaviSatNum(navisys byte) int {
	obs := this.GetNaviObs(navisys)
	return len(obs.lst)
}

func (this *GnssSVEpoch) StatSatText() string {
	var sb utils.BytesBuilder
	for i := U8_0; i < obsmap_len; i++ {
		n := 0
		c2 := 0
		c3 := 0
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			if e.NaviSys == SYS_BD {
				if e.Satid <= 16 {
					c2++
				} else {
					c3++
				}
			}
			n++
			return true
		})
		if obslst.NaviSys == SYS_BD {
			if c2 > 0 {
				sb.Appendf("%s:%d ", "C2", c2)
			}
			if c3 > 0 {
				sb.Appendf("%s:%d ", "C3", c3)
			}
		} else if n > 0 {
			sb.Appendf("%s:%d ", GetNavSysCode(i+1), n)
		}
	}
	return sb.String()
}

func (this *GnssSVEpoch) StatSatNum() string {
	var sb utils.BytesBuilder
	for i := U8_0; i < obsmap_len; i++ {
		n := 0
		c2 := 0
		c3 := 0
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			if e.NaviSys == SYS_BD {
				if e.Satid <= 16 {
					c2++
				} else {
					c3++
				}
			}
			n++
			return true
		})
		if obslst.NaviSys == SYS_BD {
			if c2 > 0 {
				sb.Appendf("%s:%d ", "C2", c2)
			}
			if c3 > 0 {
				sb.Appendf("%s:%d ", "C3", c3)
			}
			sb.Appendf("%s:%d ", GetNavSysCode(i+1), n)
		} else if n > 0 {
			sb.Appendf("%s:%d ", GetNavSysCode(i+1), n)
		}
	}
	return sb.String()
}

func (this *GnssSVEpoch) StatSignalEx(cb func(navisys byte, singaltext string, singalstr string, signalid byte, flag byte)) {
	for i := U8_0; i < obsmap_len; i++ {
		var c2, c3, sigs string
		obslst := this.obsMap[i]
		obslst.Range(func(src *GnssSVObs) bool {
			src.RangeSignal(func(signal *GnssSVSignal) bool {
				signalstr := signal.GetSignalCode()
				s := fmt.Sprintf("%s(%d)", signalstr, signal.SigId)
				if src.NaviSys == SYS_BD {
					if src.Satid <= 16 {
						if strings.Index(c2, s) == -1 {
							c2 += s + " "
							cb(src.NaviSys, s, signalstr, signal.SigId, 2)
						}
					} else {
						if strings.Index(c3, s) == -1 {
							c3 += s + " "
							cb(src.NaviSys, s, signalstr, signal.SigId, 3)
						}
					}
				}
				if strings.Index(sigs, s) == -1 {
					sigs += s + " "
					cb(src.NaviSys, s, signalstr, signal.SigId, 0)
				}
				return true
			})
			return true
		})
	}
}

func (this *GnssSVEpoch) StatSignal() string {

	var sb utils.BytesBuilder
	for i := U8_0; i < obsmap_len; i++ {
		var c2, c3, sigs string
		obslst := this.obsMap[i]
		obslst.Range(func(src *GnssSVObs) bool {
			src.RangeSignal(func(signal *GnssSVSignal) bool {
				signalstr := signal.GetSignalCode()
				s := fmt.Sprintf("%s(%d)", signalstr, signal.SigId)
				if src.NaviSys == SYS_BD {
					if src.Satid <= 16 {
						if strings.Index(c2, s) == -1 {
							c2 += s + " "
						}
					} else {
						if strings.Index(c3, s) == -1 {
							c3 += s + " "
						}
					}
				}
				if strings.Index(sigs, s) == -1 {
					sigs += s + " "
				}
				return true
			})
			return true
		})
		if obslst.NaviSys == SYS_BD {
			if len(c2) > 0 {
				sb.Appendf("C2:%s ", c2)
			}
			if len(c3) > 0 {
				sb.Appendf("C3:%s ", c3)
			}
		} else if len(sigs) > 0 {
			sb.Appendf("%s:%s ", GetNavSysCode(i+1), sigs)
		}

	}
	return sb.String()
}

func (this *GnssSVEpoch) GetNaviObs(navisys byte) *GnssSVObsList {
	return this.obsMap[navisys-1]
}

func (this *GnssSVEpoch) Clone() (dst *GnssSVEpoch) {
	dst = NewGnssSVEpoch()
	dst.CopyFrom(this)
	return

}

func (this *GnssSVEpoch) CopyFrom(src *GnssSVEpoch) {
	this.TOW = src.TOW
	this.Week = src.Week
	this.ExternalClk = src.ExternalClk
	this.ClkSteering = src.ClkSteering
	this.IODS = src.IODS
	this.SmoothingInterval = src.SmoothingInterval
	this.SmoothingType = src.SmoothingType
	this.StaID = src.StaID
	for i := U8_0; i < byte(len(this.obsMap)); i++ {
		obslst := src.obsMap[i]
		obslst.Range(func(src *GnssSVObs) bool {
			dst := this.CheckGetRawObs(src.NaviSys, src.Satid)
			dst.CopyFrom(src)
			return true
		})
	}
}

func (this *GnssSVEpoch) CopyHead(src *GnssSVEpoch) {
	this.TOW = src.TOW
	this.Week = src.Week
	this.ExternalClk = src.ExternalClk
	this.ClkSteering = src.ClkSteering
	this.IODS = src.IODS
	this.SmoothingInterval = src.SmoothingInterval
	this.SmoothingType = src.SmoothingType
	this.StaID = src.StaID
}

func (this *GnssSVEpoch) CopySysObs(src *GnssSVEpoch, sys byte) int {
	obslst := src.GetNaviObs(sys)
	if obslst == nil {
		return 0
	}
	n := 0
	obslst.Range(func(src *GnssSVObs) bool {
		dst := this.CheckGetRawObs(src.NaviSys, src.Satid)
		dst.CopyFrom(src)
		n++
		return true
	})
	return n
}

func (this *GnssSVEpoch) CompareFunc(slave *GnssSVEpoch, onCompare func(obs_m, obs_s *GnssSVObs, sig_m, sig_s *GnssSVSignal) bool) {
	var sb utils.BytesBuilder
	ok := false
	for i := U8_0; i < obsmap_len; i++ {
		om := this.obsMap[i]
		os := slave.GetNaviObs(om.NaviSys)
		if om.SatCount() == 0 {
			continue
		}

		om.Range(func(e *GnssSVObs) bool {
			sobs := os.FindObs(e.Satid)
			ok = onCompare(e, sobs, nil, nil)
			if sobs == nil || !ok {
				return ok
			}

			var ss GnssSVSignal
			sb.Reset()
			sb.Appendf("[%s]观测值差异", e.GetPrnCode())
			e.RangeSignal(func(signal *GnssSVSignal) bool {
				ss.Reset()
				sobs.GetSignalBySigID(signal.SigId, &ss)
				ok = onCompare(e, sobs, signal, &ss)
				return ok
			})

			return ok

		})
	}
}

func (this *GnssSVEpoch) Compare(slave *GnssSVEpoch, outputfn func(msg string)) {
	if this.Week != slave.Week {
		outputfn(fmt.Sprintf("时间差异:Week(%d!=%d)", this.Week, slave.Week))
	}

	if this.TOW != slave.TOW {
		outputfn(fmt.Sprintf("时间差异:TOW(%d!=%d)", this.TOW, slave.TOW))
		return
	}

	var sb utils.BytesBuilder
	for i := U8_0; i < obsmap_len; i++ {
		om := this.obsMap[i]
		os := slave.GetNaviObs(om.NaviSys)
		if om.SatCount() == 0 {
			continue
		}

		om.Range(func(e *GnssSVObs) bool {
			sobs := os.FindObs(e.Satid)
			if sobs == nil {
				outputfn(fmt.Sprintf("[%s]缺失卫星观测数据", e.GetPrnCode()))
				return true
			}

			var ss GnssSVSignal
			sb.Reset()
			sb.Appendf("[%s]观测值差异", e.GetPrnCode())
			n := 0
			e.RangeSignal(func(signal *GnssSVSignal) bool {
				ss.Reset()
				sobs.GetSignalBySigID(signal.SigId, &ss)

				if signal.IsValid() && !ss.IsValid() {
					sb.Appendf(",频点[%s]缺失C/L/S: %s%s %.2f", signal.GetSignalCode(), RinexFormatFloat(signal.PsV), RinexFormatFloat(signal.PhV), signal.Cnr)
					n++
					return true
				}

				psv := signal.PsV - ss.PsV
				if math.Abs(psv) > 0.009 {
					sb.Appendf(",C%s:%.4f-%.4f=%.4f", signal.GetSignalCode(), signal.PsV, ss.PsV, psv)
					n++
				}

				phv := signal.PhV - ss.PhV
				if math.Abs(phv) > 0.003 {
					sb.Appendf(",L%s: %.4f-%.4f=%.4f", signal.GetSignalCode(), signal.PhV, ss.PhV, phv)
					n++
				}

				cnr := signal.Cnr - ss.Cnr
				if math.Abs(float64(cnr)) > 1 {
					sb.Appendf(",S%s: %.2f-%.2f=%.2f", signal.GetSignalCode(), signal.Cnr, ss.Cnr, cnr)
					n++
				}

				// msm4 最大值
				if signal.LkT < 524289 || ss.LkT < 524289 {
					if signal.LkT != ss.LkT {
						sb.Appendf(",%s(LkT) %d!=%d", signal.GetSignalCode(), signal.LkT, ss.LkT)
						n++
					}
				}

				if signal.HalfAmb != ss.HalfAmb {
					sb.Appendf(",%s(HalfAmb) %d!=%d", signal.GetSignalCode(), signal.HalfAmb, ss.HalfAmb)
					n++
				}
				return true
			})
			if n > 0 {
				outputfn(sb.String())
			}

			return true

		})
	}
}

func (this *GnssSVEpoch) CopySig(navisys, sigIdSrc, sigIdDest byte) {
	obslst := this.GetNaviObs(navisys)
	obslst.Range(func(e *GnssSVObs) bool {
		e.CopySig(sigIdSrc, sigIdDest)
		return true
	})
}

func (this *GnssSVEpoch) MoveSig(navisys, sigIdSrc, sigIdDest byte) {
	obslst := this.GetNaviObs(navisys)
	obslst.Range(func(e *GnssSVObs) bool {
		e.CopySig(sigIdSrc, sigIdDest)
		e.EmptySig(sigIdSrc)
		return true
	})
}

func (this *GnssSVEpoch) SignalExists(navisys, sigID byte) bool {
	obslst := this.GetNaviObs(navisys)
	rval := false
	obslst.Range(func(e *GnssSVObs) bool {
		if e.IsSignalValidIdx(sigID - 1) {
			rval = true
		}
		return !rval
	})
	return rval
}

func (this *GnssSVEpoch) PhaseCheckShift(navisys, sig byte, phaseshift float64) {
	obslst := this.GetNaviObs(navisys)
	obslst.Range(func(e *GnssSVObs) bool {

		if e.IsSignalValidIdx(sig - 1) {
			phv := e.PhRngV[sig-1]
			e.PhRngV[sig-1] = phv + phaseshift
		}
		return true
	})
}

func (this *GnssSVEpoch) Range0(cb func(navisys byte, obslst *GnssSVObsList) bool) {
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		if obslst != nil {
			if !cb(obslst.NaviSys, obslst) {
				break
			}
		}
	}
}

func (this *GnssSVEpoch) CopySatObs(src *GnssSVObs) bool {
	obs := this.CheckGetRawObs(src.NaviSys, src.Satid)
	if obs == nil {
		return false
	}
	obs.CopyFrom(src)
	return true
}

func (this *GnssSVEpoch) SetSignal(src *GnssSVSignal) bool {
	obs := this.CheckGetRawObs(src.NaviSys, src.SatId)
	if obs == nil {
		return false
	}
	obs.SetSignal(src)
	return true
}

func (this *GnssSVEpoch) RangeAll(cb func(navisys byte, satid uint16, obs *GnssSVObs) bool) {
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			return cb(e.NaviSys, e.Satid, e)
		})
	}
}

func (this *GnssSVEpoch) Range(cb func(navisys byte, yobs *GnssSVObs) bool) {
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			return cb(e.NaviSys, e)
		})
	}
}

func (this *GnssSVEpoch) GetNaviSysList() string {
	var bb strings.Builder
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		if obslst != nil && obslst.SatCount() > 0 {
			bb.WriteString(GetNavSysCode(obslst.NaviSys))
		}
	}
	return bb.String()
}

func (this *GnssSVEpoch) RangeSys(naviSys byte, cb func(obs *GnssSVObs) bool) {
	obslst := this.GetNaviObs(naviSys)
	if obslst != nil && obslst.SatCount() > 0 {
		obslst.Range(cb)
	}

}

func (this *GnssSVEpoch) CheckGetRawObs(naviSys byte, satid uint16) *GnssSVObs {
	if int(naviSys) > len(this.obsMap) || naviSys == 0 || satid == 0 {
		return nil
	}
	return this.obsMap[naviSys-1].CheckGetObs(satid)
}

func (this *GnssSVEpoch) GetRawObs(naviSys byte, satid uint16) *GnssSVObs {
	if int(naviSys) > len(this.obsMap) || naviSys == 0 || satid == 0 {
		return nil
	}
	return this.obsMap[naviSys-1].FindObs(satid)
}

func (this *GnssSVEpoch) GetSingal(naviSys byte, satid uint16, sigcode string) (rval *GnssSVSignal) {
	if int(naviSys) > len(this.obsMap) || naviSys == 0 {
		return nil
	}
	obs := this.GetRawObs(naviSys, satid)
	if obs == nil {
		return nil
	}

	obs.RangeSignalCopy(func(signal *GnssSVSignal) bool {
		if signal.GetSignalCode() == sigcode {
			rval = signal
		}
		return rval == nil
	})
	return
}

func (this *GnssSVEpoch) SetRoughR(naviSys byte, satId uint16, v float64, prr int16) {
	obs := this.CheckGetRawObs(naviSys, satId)
	obs.RoughPRR = prr
	obs.RoughR = v
}

func (this *GnssSVEpoch) Valid() (rval bool) {
	rval = false
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			rval = e.IsValid()
			return !rval
		})
	}
	return rval
}

func (this *GnssSVEpoch) SetObsSignal(osig *GnssSVSignal) {
	if osig.SatId == 0 {
		return
	}

	obs := this.CheckGetRawObs(osig.NaviSys, osig.SatId)
	obs.SetSignal(osig)
}

func (this *GnssSVEpoch) DeleteNaviSysFunc(chk func(sys byte) bool) (n int) {
	for i := U8_0; i < SATID_MAX; i++ {
		v := this.obsMap[i-1]
		if v != nil && chk(v.NaviSys) {
			v.Reset()
			n++
		}
	}
	return
}

func (this *GnssSVEpoch) DeleteSatFunc(chk func(sys byte, satid uint16) bool) (n int) {
	for i := U8_0; i < SATID_MAX; i++ {
		v := this.obsMap[i-1]
		if v != nil {
			n += v.DeleteFunc(func(satid uint16) bool {
				return chk(v.NaviSys, satid)
			})
		}
	}
	return
}

func (this *GnssSVEpoch) DelSats(naviSys byte, startPrn, endPrn uint16) {
	if naviSys > obsmap_len || naviSys == 0 {
		return
	}

	obslst := this.GetNaviObs(naviSys)
	if obslst != nil {
		for satid := startPrn; satid <= endPrn; satid++ {
			obslst.Delete(satid)
		}
	}
}

func (this *GnssSVEpoch) String() string {
	var bb utils.BytesBuilder
	for i := U8_0; i < obsmap_len; i++ {
		obslst := this.obsMap[i]
		obslst.Range(func(e *GnssSVObs) bool {
			e.Write(&bb)
			return true
		})
	}
	return bb.String()
}
